#include <iostream>
#include <queue>
#include "Thread.h"
#include "Mutex.h"

namespace concurrent {

class Producer : public Thread
{
public:
	Producer(Mutex *mutex, std::queue<int> *queue);

protected:
	void run();

private:
	Mutex *_mutex;
	std::queue<int> *_queue;
};

class Consumer : public Thread
{
public:
	Consumer(Mutex *mutex, std::queue<int> *queue);

protected:
	void run();

private:
	Mutex *_mutex;
	std::queue<int> *_queue;
};

Producer::Producer(Mutex *mutex, std::queue<int> *queue)
	: _mutex(mutex), _queue(queue) {
}

void Producer::run() {
	int i = 0;
	while ( 1 ) {
		if ( _mutex->lock() ) {
			_queue->push(i);
			std::cout << "Producing: " << i << " (" << _queue->size() << ")" << std::endl;
			if ( !_mutex->unlock() ) {
				std::cout << "Released Failed" << std::endl;
			}
 			i++;
			i %= 10;
		}
		sleep(500);
	}
}

Consumer::Consumer(Mutex *mutex, std::queue<int> *queue)
	: _mutex(mutex), _queue(queue) {
};

void Consumer::run() {
	int i = 0;
	while ( 1 ) {
		_mutex->lock();
		if ( !_queue->empty() ) {
			i = _queue->front();
			_queue->pop();
			std::cout << "Consuming: " << i << std::endl;
		} else {
			std::cout << "empty queue\n";
		}
		_mutex->unlock();
		sleep(1000);
	}
}

}

int main(int argc, char *argv[]) {
	concurrent::Mutex mutex;
	std::queue<int> queue;

	concurrent::Producer p(&mutex, &queue);	
	concurrent::Consumer c1(&mutex, &queue);	
	concurrent::Consumer c2(&mutex, &queue);	

	p.start();
	c1.start();
	c2.start();

	while (1);
	return 0;
}

